package com.estudy.efficiency.autonumber;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.service.IService;
import com.estudy.efficiency.autonumber.enums.AutoNumberModelEnum;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 基于MybatisPlus的自动化编号
 * @author xiezhenyu
 * @date 2023/4/27 11:03
 */
public class MyBatisPlusAutoNumberGeneration<T> extends AutoNumberGeneration {

    private String column;
    private BaseMapper baseMapper;
    private IService service;

    /**
     * @param baseMapper mybatis注入的mapper
     * @param column 表格中编号的字段名
     * @param mode 自动编号生成模式
     * COMPARE: 比较模式,此模式在计算获取编号时每次都和持久层的编号进行比较,
     * 当(持久层中的编号>缓存编号)时将持久层的编号赋值给缓存编号,
     * 此模式效率较低,可以应用在编号用户可以编辑不是完全由系统生成的场景。
     * EFFICIENT: 高效模式,此模式不将缓存的编号与较持久层的编号进行比较,
     * 此模式效率高,可以应用在编号完全由系统生成的情况下。
     */
    public MyBatisPlusAutoNumberGeneration(BaseMapper<T> baseMapper, String column, AutoNumberModelEnum mode) {
        super(mode);
        this.column = column;
        this.baseMapper = baseMapper;
    }

    /**
     * @param service mybatis注入的service
     * @param column 表格中编号的字段名
     * @param mode 自动编号生成模式
     * COMPARE: 比较模式,此模式在计算获取编号时每次都和持久层的编号进行比较,
     * 当(持久层中的编号>缓存编号)时将持久层的编号赋值给缓存编号,
     * 此模式效率较低,可以应用在编号用户可以编辑不是完全由系统生成的场景。
     * EFFICIENT: 高效模式,此模式不将缓存的编号与较持久层的编号进行比较,
     * 此模式效率高,可以应用在编号完全由系统生成的情况下。
     */
    public MyBatisPlusAutoNumberGeneration(IService<T> service, String column, AutoNumberModelEnum mode) {
        super(mode);
        this.column = column;
        this.service = service;
    }

    /**
     * @param baseMapper mybatis注入的mapper
     * @param column 表格中编号的字段名
     */
    public MyBatisPlusAutoNumberGeneration(BaseMapper<T> baseMapper, String column) {
        this.column = column;
        this.baseMapper = baseMapper;
    }

    /**
     * @param service mybatis注入的service
     * @param column 表格中编号的字段名
     */
    public MyBatisPlusAutoNumberGeneration(IService<T> service, String column) {
        this.column = column;
        this.service = service;
    }

    @Override
    protected int initNumber(String prefix, String suffix) {
        int number = 0;
        String reg = formatNullString(prefix) + "([0-9]*)" + formatNullString(suffix);
        Pattern pattern = Pattern.compile(reg);
        QueryWrapper<T> like = new QueryWrapper<T>()
                .select(column)
                .apply(column + " regexp " + "\'^" + reg + "$\'")
                .orderByDesc(column)
                .last("limit 1");
        String no = null;
        try {
            if (baseMapper != null) {
                no = getNo(baseMapper.selectOne(like));
            } else {
                no = getNo(service.getOne(like));
            }
        } catch (IllegalAccessException e) {
//            e.printStackTrace();
            return number;
        }
        if (no != null) {
            Matcher matcher = pattern.matcher(no);
            while (matcher.find()) {
                number = Integer.parseInt(matcher.group(1));
                return number;
            }
        }
        return number;
    }

    /**
     * 获取属性值
     * @param no
     * @return
     * @throws IllegalAccessException
     */
    private String getNo(Object no) throws IllegalAccessException {
        if (no == null) {
            return null;
        }
        String result = null;
        Field[] declaredFields = no.getClass().getDeclaredFields();
        for (Field field : declaredFields) {
            Annotation[] declaredAnnotations = field.getDeclaredAnnotations();
            for (Annotation annotation : declaredAnnotations) {
                if(annotation instanceof TableField) {
                    TableField tableField = (TableField) annotation;
                    if (tableField.value().equals(column)) {
                        field.setAccessible(true);
                        return field.get(no).toString();
                    }
                }
            }
            if (field.getName().equals(column)) {
                field.setAccessible(true);
                result = field.get(no).toString();
            }
        }
        return result;
    }
}
